 
  

 






<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>

<!-- Mirrored from www.javapractices.com/topic/TopicAction.do?Id=161 by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:28:07 GMT -->
<!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=UTF-8"><!-- /Added by HTTrack -->
<head>
 <title>
  Java Practices -> Indicate table sort
 </title>
 <link rel="stylesheet" type="text/css" href="../stylesheet8.css" media="all">
 
 <link rel="shortcut icon" href='../images/favicon.ico' type="image/vnd.microsoft.icon">
 <meta name="description" content="Concise presentations of java programming practices, tasks, and conventions, amply illustrated with syntax highlighted code examples.">
 
 <meta name='keywords' content='JTable,SortBy,SortOrder,TableSortIndicator,sort,swing,java,java programming,java practices,java idiom,java style,java design patterns,java coding conventions,'>
 
 
</head>
 
<body>


<div class='menu-bar'>
 
  <a href='../home/HomeAction.html' title='Table of Contents'>Home</a> |
  <a href='../vote/VoteSummaryAction-2.html' title='View Poll Results'>Poll</a> |
   
  <A href='../feedback/FeedbackAction451f-2.html?Operation=Show' title='Send Your Feedback'>Wiki</a> |
  <b><a href='../source/SourceAction-2.html' title='Grab Source Code'>Source Code</a></b><IMG class='no-margin' SRC="../images/goldstar.gif" ALT=""> |

  <a href='http://www.web4j.com/Java_Web_Application_Framework_Overview.jsp?From=1' title='Free Download - Java Web Application Framework'><b>WEB4J</b></a> |
  
  <a href='http://www.date4j.net/' title='Replacement for java.util.Date'><b>DATE4J</b></a> |

   <a href='../references/ReferencesAction-2.html' title='References'>Links</a>
   
  <form action='http://www.javapractices.com/search/SearchAction.do' method='get' class='search-form'>
   <input type='text' name='SearchTerms' value="" size=12 maxlength=50 class='search'>
   <input type='submit' value="Search">
  </form>
 
</div>

<P>



  

 






<p class="display-messages">

 

 

</p>


<div class="main-layout">
 
   

 




<div class='page-title'>Indicate table sort</div>

<div class='main-body'>
 
<br>Sorting table rows is a common task in Swing applications.

<p>The following is an <em>extended</em> example of a reusable scheme for adding a sort icon to 
table column headers. It uses three classes.
<p><tt>SortOrder</tt> is a <a href="TopicAction2161-2.html">type-safe enumeration</a>
for the two directions of sorting (ascending and descending) :
<br>
<PRE>

<span class='keyword'>package</span> hirondelle.stocks.table;

<span class='comment'>/** 
* Enumeration class for the two directions which a sort may take.
*/</span>
<span class='keyword'>public</span> <span class='keyword'>enum</span> SortOrder  {  

  DESCENDING(<span class='literal'>"Descending"</span>),
  ASCENDING(<span class='literal'>"Ascending"</span>);

  <span class='keyword'>@Override</span> <span class='keyword'>public</span> String toString() { 
    <span class='keyword'>return</span> fName;  
  } 

  <span class='comment'>/**
  * Return the opposite &lt;tt&gt;SortOrder&lt;/tt&gt; from &lt;tt&gt;this&lt;/tt&gt; one.
  */</span>
  <span class='keyword'>public</span> SortOrder toggle(){
    <span class='keyword'>return</span> (<span class='keyword'>this</span> == ASCENDING ? DESCENDING : ASCENDING);
  }
  
  <span class='comment'>// PRIVATE //
</span>  <span class='keyword'>private</span> <span class='keyword'>final</span> String fName;
  <span class='keyword'>private</span> SortOrder(String aName){
    fName = aName;
  }
} 
</PRE>
<br>
<br><tt>SortBy</tt> is a data-centric class which holds two items :
<ul>
<li>
a <tt>SortOrder</tt></li>

<li>
an index representing a <tt>JTable</tt> column.</li>
</ul>
<br>
<PRE>

<span class='keyword'>package</span> hirondelle.stocks.table;

<span class='keyword'>import</span> hirondelle.stocks.util.HashCodeUtil;
<span class='keyword'>import</span> hirondelle.stocks.util.EqualsUtil;

<span class='comment'>/** 
* Data-centric, immutable value class representing both the direction 
* of a sort and the index of its column.
*
* &lt;P&gt;&lt;tt&gt;SortBy&lt;/tt&gt; does not have any knowledge of table contents, so it may be 
* used with any sorted table.
*/</span>
<span class='keyword'>public</span> <span class='keyword'>final</span> <span class='keyword'>class</span> SortBy  { 

  <span class='comment'>/**
  * Constructor. 
  *  
  * @param aSortOrder satisfies &lt;tt&gt;aSortOrder!=null&lt;/tt&gt; and denotes ascending 
  * or descending order.
  * @param aColumn satisfies &lt;tt&gt;aColumn &gt;= 0&lt;/tt&gt; and is the index of a 
  * table column.
  */</span>
  <span class='keyword'>public</span> SortBy (SortOrder aSortOrder, <span class='keyword'>int</span> aColumn) {
    fSortOrder = aSortOrder;
    fColumn = aColumn;
    validateState();
  }

  <span class='comment'>/**
  * Return the sense of the sort, either ascending or descending.
  */</span>
  <span class='keyword'>public</span> SortOrder getOrder() {
    <span class='keyword'>return</span> fSortOrder;
  }
  
  <span class='comment'>/**
  * Return the column index identifying the sort, or {@link #NO_SELECTED_COLUMN}
  * in the case of {@link #NONE}. 
  */</span>
  <span class='keyword'>public</span> <span class='keyword'>int</span> getColumn() {
    <span class='keyword'>return</span> fColumn;
  }
  
  <span class='comment'>/**
  * A special &lt;tt&gt;SortBy&lt;/tt&gt; which represents the absence of any sort.
  */</span>
  <span class='keyword'>public</span> <span class='keyword'>static</span> <span class='keyword'>final</span> SortBy NONE = <span class='keyword'>new</span> SortBy();
  
  <span class='comment'>/**
  * The special value of the column index used by {@link #NONE}. 
  */</span>
  <span class='keyword'>public</span> <span class='keyword'>static</span> <span class='keyword'>final</span> <span class='keyword'>int</span> NO_SELECTED_COLUMN = -<span class='literal'>1</span>;

  <span class='comment'>/**
  * Represent this object as a &lt;tt&gt;String&lt;/tt&gt; - intended 
  * for logging purposes only.
  */</span>
  <span class='keyword'>@Override</span> <span class='keyword'>public</span> String toString() {
    StringBuilder result = <span class='keyword'>new</span> StringBuilder();
    String newLine = System.getProperty(<span class='literal'>"line.separator"</span>);
    result.append( <span class='keyword'>this</span>.getClass().getName() );
    result.append(<span class='literal'>" {"</span>);
    result.append(newLine);

    result.append(<span class='literal'>" fSortOrder = "</span>).append(fSortOrder).append(newLine);
    result.append(<span class='literal'>" fColumn = "</span>).append(fColumn).append(newLine);
    
    result.append(<span class='literal'>"}"</span>);
    result.append(newLine);
    <span class='keyword'>return</span> result.toString();
  }

  <span class='keyword'>@Override</span> <span class='keyword'>public</span> <span class='keyword'>boolean</span> equals( Object aThat ) {
    <span class='keyword'>if</span> ( <span class='keyword'>this</span> == aThat ) <span class='keyword'>return</span> <span class='keyword'>true</span>;
    <span class='keyword'>if</span> ( !(aThat <span class='keyword'>instanceof</span> SortBy) ) <span class='keyword'>return</span> <span class='keyword'>false</span>;
    SortBy that = (SortBy)aThat;
    <span class='keyword'>return</span> 
      EqualsUtil.areEqual(<span class='keyword'>this</span>.fSortOrder, that.fSortOrder) &amp;&amp;
      EqualsUtil.areEqual(<span class='keyword'>this</span>.fColumn, that.fColumn)
    ;
  }

  <span class='keyword'>@Override</span> <span class='keyword'>public</span> <span class='keyword'>int</span> hashCode() {
    <span class='keyword'>int</span> result = HashCodeUtil.SEED;
    result = HashCodeUtil.hash( result, fSortOrder );
    result = HashCodeUtil.hash( result, fColumn );
    <span class='keyword'>return</span> result;
  }

  <span class='comment'>// PRIVATE // 
</span>  <span class='keyword'>private</span> <span class='keyword'>final</span> SortOrder fSortOrder;
  <span class='keyword'>private</span> <span class='keyword'>final</span> <span class='keyword'>int</span> fColumn;
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> validateState() {
    <span class='keyword'>boolean</span> hasValidState = (fSortOrder!=<span class='keyword'>null</span>) &amp;&amp; (fColumn >= <span class='literal'>0</span>);
    <span class='keyword'>if</span> ( !hasValidState ) <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalArgumentException(<span class='keyword'>this</span>.toString());
  }
  
  <span class='comment'>/**
  * Constructor used only for the special case of no sort at all.
  *
  * &lt;P&gt;Note that this constructor does not perform the validations done by the public 
  * constructor, and is thus not subject to the same restrictions.
  */</span>
  <span class='keyword'>private</span> SortBy(){
    fSortOrder = SortOrder.DESCENDING;
    fColumn = NO_SELECTED_COLUMN;
  }
} 
</PRE>
<br>
<br>Finally, <tt>TableSortIndicator</tt>
<ul>
<li>
listens for clicks on column headers</li>

<li>
manages an icon indicating sort column and sort direction</li>

<li>
toggles direction of this icon when clicked repeatedly</li>

<li>
notifies listeners of new sort selections</li>
</ul>
Tables (or their models) keep the responsibility for performing the actual
sort, but delegate the task of listening for mouse clicks and managing
sort icons to <tt>TableSortIndicator</tt>. Tables are informed of changes
to the desired sort by acting as an <tt>Observer</tt> of <tt>TableSortIndicator</tt>,
and by using its <tt>TableSortIndicator.getSortBy</tt> method
<br>
<PRE>

<span class='keyword'>package</span> hirondelle.stocks.table;

<span class='keyword'>import</span> java.util.*;
<span class='keyword'>import</span> javax.swing.*;
<span class='keyword'>import</span> javax.swing.table.*;
<span class='keyword'>import</span> java.awt.*;
<span class='keyword'>import</span> java.awt.event.*;

<span class='keyword'>import</span> hirondelle.stocks.util.Consts;
<span class='keyword'>import</span> hirondelle.stocks.util.Args;

<span class='comment'>/**
* Places an up or down icon in a table column header, as an indicator of the 
* primary sort.
*
*&lt;P&gt;This class does not do any sorting of the underlying rows - it merely indicates
* the identity and direction of the sorted column.
*
* &lt;P&gt;The user changes the indicated sort by simply clicking on a column header.
* The initial click always indicates a descending sort. 
* A re-click on the same column will toggle the indicated direction. 
*
* &lt;P&gt;Listeners to this class are notified when the sort has changed, and 
* use {@link #getSortBy} to retrieve the new sort, and then perform the actual
* sorting. Example :
&lt;pre&gt;
  TableSortIndicator fSortIndicator = new TableSortIndicator(table, upIcon, downIcon);
  fSortIndicator.addObserver(this);
  //when the user clicks a column header, fSortIndicator will notify 
  //registered observers, who will call getSortBy to fetch the new sort.
  //..
  public void update(Observable aObservable, Object aData) {
    //extract column and (asc|desc) from fSortIndicator
    SortBy sortBy = fSortIndicator.getSortBy();
    //...perform the actual sorting
  }
&lt;/pre&gt; 
* Instead of using a mouse click, the sort can be set programmatically as well; this 
* is useful for reflecting a sort selected through a preferences dialog. Example :
 &lt;pre&gt;
  TableSortIndicator fSortIndicator = new TableSortIndicator(table, upIcon, downIcon);
  fSortIndicator.addObserver(this);
  fSortIndicator.setSortBy( sortByPreference ) ; //setSortBy calls the update method
&lt;/pre&gt; 
*/</span>
<span class='keyword'>final</span> <span class='keyword'>class</span> TableSortIndicator <span class='keyword'>extends</span> Observable {
  
  <span class='comment'>/**
  * Constructor.
  *  
  * @param aTable receives indication of table sort ; if it has any custom 
  * header renderers, they will be overwritten by this class.
  * @param aUpIcon placed in column header to indicate ascending sort.
  * @param aDownIcon placed in column header to indicate descending sort.
  */</span>
  TableSortIndicator(JTable aTable, Icon aUpIcon, Icon aDownIcon) {
    Args.checkForNull(aUpIcon);
    Args.checkForNull(aDownIcon);
    
    fTable = aTable;
    fUpIcon = aUpIcon;
    fDownIcon = aDownIcon;
    fCurrentSort = SortBy.NONE;
    
    initHeaderClickListener();    
    initHeaderRenderers();
    assert getRenderer(<span class='literal'>0</span>) != <span class='keyword'>null</span> : <span class='literal'>"Ctor - renderer 0 is null."</span>;
  }
  
  <span class='comment'>/**
  * Return the identity of column having the primary sort, and the direction
  * of its sort.
  */</span>
  SortBy getSortBy(){
    <span class='keyword'>return</span> fCurrentSort;
  }
  
  <span class='comment'>/**
  * Change the sort programmatically, instead of through a user click.
  *
  * &lt;P&gt;If there is a user preference for sort, it is passed to this method.
  * Notifies listeners of the change to the sort.
  */</span>
  <span class='keyword'>void</span> setSortBy( SortBy aTargetSort ){
    validateIdx( aTargetSort.getColumn() );
    initHeaderRenderers();
    assert getRenderer(<span class='literal'>0</span>) != <span class='keyword'>null</span> : <span class='literal'>"setSortBy - renderer 0 is null."</span>;
    fTargetSort = aTargetSort;
    
    <span class='keyword'>if</span> ( fCurrentSort == SortBy.NONE ){
      setInitialHeader();
    }
    <span class='keyword'>else</span> <span class='keyword'>if</span> ( 
      fCurrentSort.getColumn() == fTargetSort.getColumn() &amp;&amp; 
      fCurrentSort.getOrder() != fTargetSort.getOrder() 
    ) {
      toggleIcon();
    }
    <span class='keyword'>else</span> {
      updateTwoHeaders();
    }
    synchCurrentSortWithSelectedSort();
    notifyAndPaint();
  }
  
  <span class='comment'>// PRIVATE //
</span>  <span class='keyword'>private</span> <span class='keyword'>final</span> JTable fTable;
  <span class='keyword'>private</span> <span class='keyword'>final</span> Icon fUpIcon;
  <span class='keyword'>private</span> <span class='keyword'>final</span> Icon fDownIcon;
  
  <span class='comment'>/**
  * The sort as currently displayed to the user, representing the end result of a 
  * previous user request.
  */</span>
  <span class='keyword'>private</span> SortBy fCurrentSort;
  
  <span class='comment'>/**
  * A new sort to be processed, whose origin is either a user preference or a 
  * a mouse click.
  *
  * Once fTargetSort is processed, fCurrentSort is assigned to fTargetSort.
  */</span>
  <span class='keyword'>private</span> SortBy fTargetSort;
  
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> SortOrder fDEFAULT_SORT_ORDER = SortOrder.DESCENDING;
  
  <span class='comment'>/**
  * Return true only if the index is in the range 0..N-1, where N is the 
  * number of columns in fTable.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>boolean</span> isValidColumnIdx(<span class='keyword'>int</span> aColumnIdx) {
    <span class='keyword'>return</span> <span class='literal'>0</span> <= aColumnIdx &amp;&amp; aColumnIdx <= fTable.getColumnCount()-<span class='literal'>1</span> ;
  }

  <span class='keyword'>private</span> <span class='keyword'>void</span> validateIdx(<span class='keyword'>int</span> aSelectedIdx) {
    <span class='keyword'>if</span> ( ! isValidColumnIdx(aSelectedIdx) ) {
      <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalArgumentException(<span class='literal'>"Column index is out of range: "</span> + aSelectedIdx);
    }
  }
  
  <span class='comment'>/**
  * Called both upon construction and by {@link #setSortBy}.
  *  
  * If fireTableStructureChanged is called, then the original headers are lost, and 
  * this method must be called in order to restore them.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> initHeaderRenderers(){
    <span class='comment'>/*
    * Attach a default renderer explicitly to all columns. This is a 
    * workaround for the unusual fact that TableColumn.getHeaderRenderer returns 
    * null in the default case; Sun did this as an optimization for tables with 
    * very large numbers of columns. As well, there is only one default renderer
    * instance which is reused by each column.
    * See http://developer.java.sun.com/developer/bugParade/bugs/4276838.html for 
    * further information.
    */</span>
    <span class='keyword'>for</span> (<span class='keyword'>int</span> idx=<span class='literal'>0</span>; idx &lt; fTable.getColumnCount(); ++idx) {
      TableColumn column = fTable.getColumnModel().getColumn(idx);
      column.setHeaderRenderer( <span class='keyword'>new</span> Renderer(fTable.getTableHeader()) );
      assert column.getHeaderRenderer() != <span class='keyword'>null</span> : <span class='literal'>"Header Renderer is null"</span>;
    }
  }
  
  <span class='keyword'>private</span> Renderer getRenderer(<span class='keyword'>int</span> aColumnIdx) {
    TableColumn column = fTable.getColumnModel().getColumn(aColumnIdx);
    <span class='keyword'>return</span> (Renderer)column.getHeaderRenderer();
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> initHeaderClickListener() {
    fTable.getTableHeader().addMouseListener( <span class='keyword'>new</span> MouseAdapter() {
      <span class='keyword'>public</span> <span class='keyword'>void</span> mouseClicked(MouseEvent event) {
        <span class='keyword'>int</span> selectedIdx = fTable.getColumnModel().getColumnIndexAtX(event.getX());
        processClick( selectedIdx );
      }
    });
  }

  <span class='comment'>/**
  * Update the display of table headers to reflect a new sort, as indicated by a 
  * mouse click performed by the user on a column header.
  *
  * If &lt;tt&gt;aSelectedIdx&lt;/tt&gt; is the column which already has the sort indicator, 
  * then toggle the indicator to its opposite state (up -&gt; down, down -&gt; up). 
  * If &lt;tt&gt;aSelectedIdx&lt;/tt&gt; does not already display a sort indicator, then 
  * add a down indicator to it, and remove the indicator from the fCurrentSort 
  * column, if present.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> processClick(<span class='keyword'>int</span> aSelectedIdx){
    validateIdx( aSelectedIdx );
    
    <span class='keyword'>if</span> ( fCurrentSort.getColumn() == aSelectedIdx ) {
      fTargetSort = <span class='keyword'>new</span> SortBy( fCurrentSort.getOrder().toggle(), aSelectedIdx);
    }
    <span class='keyword'>else</span> {
      fTargetSort = <span class='keyword'>new</span> SortBy(fDEFAULT_SORT_ORDER , aSelectedIdx);
    }
    
    <span class='keyword'>if</span> ( fCurrentSort == SortBy.NONE ){
      setInitialHeader();
    }
    <span class='keyword'>if</span> ( fCurrentSort.getColumn() == fTargetSort.getColumn() ) {
      toggleIcon();
    }
    <span class='keyword'>else</span> {
      updateTwoHeaders();
    }
    
    synchCurrentSortWithSelectedSort();
    notifyAndPaint();
  }

  <span class='keyword'>private</span> <span class='keyword'>void</span> notifyAndPaint(){
    setChanged();
    notifyObservers();
    fTable.getTableHeader().resizeAndRepaint();
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> setInitialHeader(){
    <span class='keyword'>if</span> ( fTargetSort.getOrder() == SortOrder.DESCENDING ){
      getRenderer( fTargetSort.getColumn() ).setIcon(fDownIcon);
    }
    <span class='keyword'>else</span> {
      getRenderer( fTargetSort.getColumn() ).setIcon(fUpIcon);
    }
  }

  <span class='comment'>/**
  * Flip the direction of the icon (up-&gt;down or down-&gt;up).
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> toggleIcon(){
    Renderer renderer = getRenderer(fCurrentSort.getColumn());
    <span class='keyword'>if</span> ( fCurrentSort.getOrder() == SortOrder.ASCENDING ) {
      renderer.setIcon(fDownIcon);
    }
    <span class='keyword'>else</span> {
      renderer.setIcon(fUpIcon);
    }
  }
  
  <span class='comment'>/**
  * Change the fCurrentSort column to having no icon, and change the fTargetSort 
  * column to having a down icon.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> updateTwoHeaders() {
    getRenderer(fCurrentSort.getColumn()).setIcon(<span class='keyword'>null</span>);
    getRenderer(fTargetSort.getColumn()).setIcon(fDownIcon);
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> synchCurrentSortWithSelectedSort(){
    fCurrentSort = fTargetSort;
  }

  <span class='comment'>/**
  * Renders a column header with an icon.
  *
  * This class duplicates the default header behavior, but there does 
  * not seem to be any other option, since such an object does not seem
  * to be available from JTableHeader.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> <span class='keyword'>class</span> Renderer <span class='keyword'>extends</span> DefaultTableCellRenderer {
    Renderer(JTableHeader aTableHeader){
      setHorizontalAlignment(JLabel.CENTER);
      setForeground(aTableHeader.getForeground());
      setBackground(aTableHeader.getBackground());
      setBorder(UIManager.getBorder(<span class='literal'>"TableHeader.cellBorder"</span>));
      fTableHeader = aTableHeader;
    }
    <span class='keyword'>public</span> Component getTableCellRendererComponent(
      JTable aTable, 
      Object aValue, 
      <span class='keyword'>boolean</span> aIsSelected,
      <span class='keyword'>boolean</span> aHasFocus, 
      <span class='keyword'>int</span> aRowIdx, 
      <span class='keyword'>int</span> aColumnIdx
    ) {    
      setText((aValue == <span class='keyword'>null</span>) ? Consts.EMPTY_STRING : aValue.toString());
      setFont(fTableHeader.getFont());
      <span class='keyword'>return</span> <span class='keyword'>this</span>;
    }
    <span class='keyword'>private</span> JTableHeader fTableHeader;
  }
}
 
</PRE>
<br>
<br>
<br>

</div>




<div class='topic-section'>See Also :</div>
<div class='main-body'>
 
  
  <a href='TopicActiond0e1-2.html?Id=162'>Sort table rows</a> <br>
 
  
  <a href='TopicAction167e-2.html?Id=163'>Filter table rows</a> <br>
 
</div>


<div class='topic-section'>Would you use this technique?</div>
<div class='main-body'>
  
  <form action="http://www.javapractices.com/vote/AddVoteAction.do" method='post'>
    Yes<input type='radio' name='Choice' value='Y' >
    &nbsp;&nbsp;No<input type='radio' name='Choice' value='N'>
    &nbsp;&nbsp;Undecided<input type='radio' name='Choice' value="?" >
    &nbsp;&nbsp;<input type=submit value="Vote" >
    <input type='hidden' name='Operation' value='Apply'>
    <input type='hidden' name='TopicId' value='161'>
  </form>
</div>

<div style='height:10.0em;'></div>

 
 
</div>

  

 





<div align='center' class='legalese'>  
&copy; 2011 Hirondelle Systems |
<a href='../source/SourceAction-2.html'><b>Source Code</b></a><IMG class='no-margin' SRC="../images/goldstar.gif" ALT=""> |
<a href="mailto:webmaster@javapractices.com">Contact</a> |
<a href="http://creativecommons.org/licenses/by-nc-sa/1.0/">License</a> |
<a href='../apps/cjp.rss'>RSS</a>
<!-- ukey="2AC36CD2" -->
<!-- ckey="16DF3D87" -->
<br>

 Individual code snippets can be used under this <a href='../LICENSE.txt'>BSD license</a> - Last updated on June 6, 2010.<br>
 Over 150,000 unique IPs last month - <span title='Java Practices 2.6.5, Mon May 16 00:00:00 EDT 2011'>Built with</span> <a href='http://www.web4j.com/'>WEB4J</a>.<br>
 - In Memoriam : Bill Dirani -
</div>

<script src="../../www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-2633428-1";
urchinTracker();
</script>



</body>

<!-- Mirrored from www.javapractices.com/topic/TopicAction.do?Id=161 by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:28:07 GMT -->
<!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=UTF-8"><!-- /Added by HTTrack -->
</html>
